﻿using System;
using System.Collections.Generic;
using Cpuss.Strategies;
using NUnit.Framework;

namespace Cpuss.Tests
{

    /// <summary>
    /// Tests for Runner.
    /// </summary>
    [TestFixture]
    public class RunnerTest
    {

        /// <summary>
        /// Check to see that the correct exception is raised when the strategy is null.
        /// </summary>
        [Test]
        [ExpectedException(typeof(ArgumentNullException))]
        public void StrategyIsNullTest()
        {
            ProcessLoad processes = new ProcessLoad()
            {
                new Process(1, 9, 0),
                new Process(2, 8, 10),
                new Process(3, 12, 16)
            };
            Runner runner = new Runner(processes, null);
        }

        /// <summary>
        /// Check to see that assigning processes to the runner works as expected.
        /// </summary>
        [Test]
        public void ReadyQueueTest()
        {
            ProcessLoad processes = new ProcessLoad()
            {
                new Process(1, 9, 0),
                new Process(2, 8, 10),
                new Process(3, 12, 16)
            };
            Runner runner = new Runner(processes, new FirstComeFirstServed());

            Assert.IsNotNull(runner.ProcessLoad);
            Assert.AreEqual(3, runner.ProcessLoad.Count);
        }

        /// <summary>
        /// Check to see that if the process load is null the correct exception is raised.
        /// </summary>
        [Test]
        [ExpectedException(typeof(ArgumentNullException))]
        public void ProcessLoadNullTest()
        {
            Runner runner = new Runner(null, new FirstComeFirstServed());
        }

        /// <summary>
        /// Check to see that using a non null List of Processes that contains no processes throws the correct exception.
        /// </summary>
        [Test]
        [ExpectedException(typeof(ArgumentException))]
        public void ProcessLoadEmptyTest()
        {
            ProcessLoad processLoad = new ProcessLoad();

            Runner actual = new Runner(processLoad, new FirstComeFirstServed());
        }

        /// <summary>
        /// Check to see that the start and completion time of a process is correct when using ProcessCompleted event.
        /// </summary>
        [Test]
        public void RunnerProcessCompletetedDataTest()
        {
            ProcessLoad processes = new ProcessLoad()
            {
                new Process(1, 9, 6)
            };
            Runner runner = new Runner(processes, new FirstComeFirstServed());

            Process actual = null;
            Process expected = new Process(1, 9, 6) { StartTime = 6, CompletionTime = 15 };

            runner.ProcessCompleted += (o, e) =>
                actual = new Process(e.Id, e.BurstTime, e.ArrivalTime) { StartTime = e.StartTime, CompletionTime = e.CompletionTime };

            runner.Run();

            Assert.AreEqual(expected.StartTime, actual.StartTime);
            Assert.AreEqual(expected.CompletionTime, actual.CompletionTime);
        }

        /// <summary>
        /// Check to see that the correct event is raised and the associated data is correct when a process utilizes the CPU for the first time.
        /// </summary>
        [Test]
        public void RunnerProcessStartTest()
        {
            ProcessLoad processes = new ProcessLoad()
            {
                new Process(1, 9, 6)
            };
            Runner runner = new Runner(processes, new FirstComeFirstServed());

            Process actual = null;
            Process expected = new Process(1, 9, 6) { StartTime = 6 };

            runner.ProcessStarted += (o, e) =>
                actual = new Process(e.Id, e.BurstTime, e.ArrivalTime) { StartTime = e.StartTime };

            runner.Run();

            Assert.AreEqual(expected.StartTime, actual.StartTime);
        }

        /// <summary>
        /// Check to see that the correct data is returned when the Runner.Started event is raised.
        /// </summary>
        [Test]
        public void RunnerStartedTest()
        {
            ProcessLoad processLoad = new ProcessLoad()
            {
                new Process(23, 67, 23),
                new Process(12, 4, 90),
                new Process(66, 2, 12)
            };
            Runner runner = new Runner(processLoad, new FirstComeFirstServed());
            RunnerStartedEventArgs actual = null;
            runner.Started += (o, e) => actual = new RunnerStartedEventArgs(e.ProcessCount, e.Strategy);

            runner.Run();

            Assert.AreEqual(3, actual.ProcessCount);
            Assert.IsNotNull(actual.Strategy);
        }

        /// <summary>
        /// Check to see that the correct data is returned when the Runner.Completed event is raised.
        /// </summary>
        [Test]
        public void RunnerCompleteTest()
        {
            ProcessLoad processLoad = new ProcessLoad()
            {
                new Process(23, 67, 3),
                new Process(12, 4, 7),
                new Process(66, 2, 9)
            };
            Runner runner = new Runner(processLoad, new FirstComeFirstServed());
            RunnerCompletedEventArgs actual = null;
            runner.Completed += (o, e) => actual = new RunnerCompletedEventArgs(e.ProcessCount, e.Strategy, e.TotalTime);

            runner.Run();

            Assert.AreEqual(3, actual.ProcessCount);
            Assert.IsNotNull(actual.Strategy);
            Assert.AreEqual(76, actual.TotalTime);
        }

        /// <summary>
        /// Check to see that the auto-gen load works correctly by checking the total number of process metrics recorded
        /// in the simulation.
        /// </summary>
        [Test]
        public void AutoGeneratedLoadTest()
        {
            Runner actual = new Runner(10, 20, 50, new FirstComeFirstServed());

            actual.Run();

            Assert.AreEqual(80, actual.Metrics.Count);
        }

        /// <summary>
        /// Check to see that the metrics collected are of the correct quantity.
        /// </summary>
        [Test]
        public void AutoGeneratedLoadMetricsPopulatedByEventsTest()
        {
            Runner runner = new Runner(10, 15, 120, new FirstComeFirstServed());
            Dictionary<int, Process> metrics = new Dictionary<int, Process>();

            runner.ProcessCompleted += (o, e) => metrics.Add(e.Id, new Process(e.Id, e.BurstTime, e.ArrivalTime)
            {
                StartTime = e.StartTime,
                CompletionTime = e.CompletionTime
            });
            runner.Run();

            Assert.AreEqual(145, metrics.Count);
        }

        /// <summary>
        /// Check to see that the correct exception is thrown when the number of small processes
        /// is negative.
        /// </summary>
        [Test]
        [ExpectedException(typeof(ArgumentOutOfRangeException))]
        public void AutoGeneratedSmallNegativeTest()
        {
            Runner actual = new Runner(-1, 4, 8, new FirstComeFirstServed());
        }

        /// <summary>
        /// Check to see that the correct exception is thrown when the number of medium processes
        /// is negative.
        /// </summary>
        [Test]
        [ExpectedException(typeof(ArgumentOutOfRangeException))]
        public void AutoGeneratedMediumNegativeTest()
        {
            Runner actual = new Runner(4, -1, 6, new FirstComeFirstServed());
        }

        /// <summary>
        /// Check to see that the correct exception is thrown when the number of large processes
        /// is negative.
        /// </summary>
        [Test]
        [ExpectedException(typeof(ArgumentOutOfRangeException))]
        public void AutoGeneratedLargeNegativeTest()
        {
            Runner actual = new Runner(3, 4, -1, new FirstComeFirstServed());
        }

        /// <summary>
        /// Check to see that the cpu activity of a very basic simulation is recorded correctly.
        /// </summary>
        [Test]
        public void CpuActivitySingleProcessTest()
        {
            ProcessLoad processLoad = new ProcessLoad()
            {
                new Process(1, 23, 0)
            };
            Runner actual = new Runner(processLoad, new FirstComeFirstServed());

            actual.Run();

            Assert.AreEqual(0, actual.Metrics[1].CpuActivity[0].First);
            Assert.AreEqual(23, actual.Metrics[1].CpuActivity[0].Second);
        }

        /// <summary>
        /// Check to see that activity is logged correctly for a process that utilizes the cpu in two
        /// seperate bursts.
        /// </summary>
        [Test]
        public void CpuActivityTwoProcessesSjfTest()
        {
            ProcessLoad processLoad = new ProcessLoad()
            {
                new Process(1, 3, 0),
                new Process(2, 1, 1)
            };
            Runner actual = new Runner(processLoad, new ShortestJobFirst(1));

            actual.Run();

            Assert.AreEqual(0, actual.Metrics[1].CpuActivity[0].First);
            Assert.AreEqual(1, actual.Metrics[1].CpuActivity[0].Second);
            Assert.AreEqual(2, actual.Metrics[1].CpuActivity[1].First);
            Assert.AreEqual(4, actual.Metrics[1].CpuActivity[1].Second);
            Assert.AreEqual(1, actual.Metrics[2].CpuActivity[0].First);
            Assert.AreEqual(2, actual.Metrics[2].CpuActivity[0].Second);
        }

        /// <summary>
        /// Check to see that the activity of each process is logged correctly.
        /// </summary>
        [Test]
        public void CpuActivityMultipleProcessesSjfNoPreemptionTest()
        {
            ProcessLoad processLoad = new ProcessLoad()
            {
                new Process(1, 2, 0),
                new Process(2, 3, 4),
                new Process(3, 1, 2),
                new Process(4, 7, 8),
                new Process(5, 9, 12)
            };
            Runner actual = new Runner(processLoad, new ShortestJobFirst(1));

            actual.Run();

            Assert.AreEqual(0, actual.Metrics[1].CpuActivity[0].First);
            Assert.AreEqual(2, actual.Metrics[1].CpuActivity[0].Second);
            Assert.AreEqual(4, actual.Metrics[2].CpuActivity[0].First);
            Assert.AreEqual(7, actual.Metrics[2].CpuActivity[0].Second);
            Assert.AreEqual(2, actual.Metrics[3].CpuActivity[0].First);
            Assert.AreEqual(3, actual.Metrics[3].CpuActivity[0].Second);
            Assert.AreEqual(8, actual.Metrics[4].CpuActivity[0].First);
            Assert.AreEqual(15, actual.Metrics[4].CpuActivity[0].Second);
            Assert.AreEqual(15, actual.Metrics[5].CpuActivity[0].First);
            Assert.AreEqual(24, actual.Metrics[5].CpuActivity[0].Second);
        }

        /// <summary>
        /// Check to see that the activity of each process is logged correctly.
        /// </summary>
        [Test]
        public void CpuActivityMutlipleProcessesMultiplePreemptionsTest()
        {
            ProcessLoad processLoad = new ProcessLoad()
            {
                new Process(1, 5, 0),
                new Process(2, 7, 0),
                new Process(3, 8, 0)
            };
            Runner actual = new Runner(processLoad, new RoundRobin(2));

            actual.Run();

            Assert.AreEqual(0, actual.Metrics[1].CpuActivity[0].First);
            Assert.AreEqual(2, actual.Metrics[1].CpuActivity[0].Second);
            Assert.AreEqual(6, actual.Metrics[1].CpuActivity[1].First);
            Assert.AreEqual(8, actual.Metrics[1].CpuActivity[1].Second);
            Assert.AreEqual(12, actual.Metrics[1].CpuActivity[2].First);
            Assert.AreEqual(13, actual.Metrics[1].CpuActivity[2].Second);
            Assert.AreEqual(2, actual.Metrics[2].CpuActivity[0].First);
            Assert.AreEqual(4, actual.Metrics[2].CpuActivity[0].Second);
            Assert.AreEqual(8, actual.Metrics[2].CpuActivity[1].First);
            Assert.AreEqual(10, actual.Metrics[2].CpuActivity[1].Second);
            Assert.AreEqual(13, actual.Metrics[2].CpuActivity[2].First);
            Assert.AreEqual(15, actual.Metrics[2].CpuActivity[2].Second);
            Assert.AreEqual(17, actual.Metrics[2].CpuActivity[3].First);
            Assert.AreEqual(18, actual.Metrics[2].CpuActivity[3].Second);
            Assert.AreEqual(4, actual.Metrics[3].CpuActivity[0].First);
            Assert.AreEqual(6, actual.Metrics[3].CpuActivity[0].Second);
            Assert.AreEqual(10, actual.Metrics[3].CpuActivity[1].First);
            Assert.AreEqual(12, actual.Metrics[3].CpuActivity[1].Second);
            Assert.AreEqual(15, actual.Metrics[3].CpuActivity[2].First);
            Assert.AreEqual(17, actual.Metrics[3].CpuActivity[2].Second);
            Assert.AreEqual(18, actual.Metrics[3].CpuActivity[3].First);
            Assert.AreEqual(20, actual.Metrics[3].CpuActivity[3].Second);
        }

        /// <summary>
        /// Check to see that the average wait time is correct.
        /// </summary>
        [Test]
        public void AverageWaitSjfTimeTest()
        {
            ProcessLoad processLoad = new ProcessLoad()
            {
                new Process(1, 6, 0),
                new Process(2, 8, 0),
                new Process(3, 7, 0),
                new Process(4, 3, 0)
            };
            Runner actual = new Runner(processLoad, new ShortestJobFirst(1));

            actual.Run();

            Assert.AreEqual(7, actual.GetWaitTimeMean());
        }

        /// <summary>
        /// Check to see that the correct time is returned.
        /// </summary>
        [Test]
        public void AverageWaitTimeSjfNoFloatingPointTest()
        {
            ProcessLoad processLoad = new ProcessLoad()
            {
                new Process(1, 4, 0),
                new Process(2, 2, 2),
                new Process(3, 1, 1)
            };
            Runner actual = new Runner(processLoad, new ShortestJobFirst(1));

            actual.Run();

            Assert.AreEqual(1, actual.GetWaitTimeMean());
        }

        /// <summary>
        /// Check to see that the correct time is returned.
        /// </summary>
        [Test]
        public void AverageWaitTimeRrNoFloatingPointTest()
        {
            ProcessLoad processLoad = new ProcessLoad()
            {
                new Process(1, 5, 2),
                new Process(2, 3, 1),
                new Process(3, 2, 3)
            };
            Runner actual = new Runner(processLoad, new RoundRobin(2));

            actual.Run();

            Assert.AreEqual((double)10/3, actual.GetWaitTimeMean());
        }

        /// <summary>
        /// Check to see that the correct value is returned.
        /// </summary>
        [Test]
        public void AverageWaitTimeSjfFloatingPointTest()
        {
            ProcessLoad processLoad = new ProcessLoad()
            {
                new Process(1, 5, 2),
                new Process(2, 3, 1),
                new Process(3, 2, 3)
            };
            Runner actual = new Runner(processLoad, new ShortestJobFirst(1));

            actual.Run();

            Assert.AreEqual((double)5/3, actual.GetWaitTimeMean());
        }

        /// <summary>
        /// Check to see that the correct exception is raised when the average wait time is aquired before the simulation
        /// has been completed.
        /// </summary>
        [Test]
        [ExpectedException(typeof(InvalidOperationException))]
        public void AverageWaitTimeBeforeSimulationHasStarted()
        {
            Runner actual = new Runner(10, 10, 10, new RoundRobin(10));

            actual.GetWaitTimeMean();
        }

        /// <summary>
        /// Check to see that the correct processes are preempted.
        /// </summary>
        [Test]
        public void ProcessPreemptedEventRoundRobinTest()
        {
            ProcessLoad processLoad = new ProcessLoad()
            {
                new Process(1, 10, 0),
                new Process(2, 12, 1),
                new Process(3, 1, 2)
            };
            Runner runner = new Runner(processLoad, new RoundRobin(5));
            List<int> actual = new List<int>();
            List<int> expected = new List<int>()
            {
                1,
                2
            };

            runner.ProcessPreempted += (o, e) => actual.Add(e.Id);
            runner.Run();

            for (int i = 0; i < expected.Count; i++)
            {
                Assert.AreEqual(expected[i], actual[i]);
            }
        }

        /// <summary>
        /// Check to see the correct processes have are preempted.
        /// </summary>
        [Test]
        public void ProcessPreemptedEventSjfTest()
        {
            ProcessLoad processLoad = new ProcessLoad()
            {
                new Process(1, 7, 0),
                new Process(2, 4, 1),
                new Process(3, 1, 2),
                new Process(4, 1, 3)
            };
            Runner runner = new Runner(processLoad, new ShortestJobFirst(1));
            List<int> actual = new List<int>();
            List<int> expected = new List<int>()
            {
                1,
                2
            };

            runner.ProcessPreempted += (o, e) => actual.Add(e.Id);
            runner.Run();

            for (int i = 0; i < expected.Count; i++)
            {
                Assert.AreEqual(expected[i], actual[i]);
            }
        }

        /// <summary>
        /// Check to see that the correct value is returned.
        /// </summary>
        [Test]
        public void GetMinimumWaitTimeTest()
        {
            ProcessLoad processLoad = new ProcessLoad()
            {
                new Process(1, 5, 0),
                new Process(2, 1, 1),
                new Process(3, 7, 2)
            };
            Runner actual = new Runner(processLoad, new RoundRobin(2));

            actual.Run();

            Assert.AreEqual(1, actual.GetMinimumWaitTime());
        }

        /// <summary>
        /// Check to see that the correct exception is thrown when calling the method
        /// before the simulation has been invoked.
        /// </summary>
        [Test]
        [ExpectedException(typeof(InvalidOperationException))]
        public void GetMinimumWaitTimeSimulationNotInvokedTest()
        {
            ProcessLoad processLoad = new ProcessLoad()
            {
                new Process(1, 5, 0),
                new Process(2, 1, 1),
                new Process(3, 7, 2)
            };
            Runner actual = new Runner(processLoad, new RoundRobin(2));

            actual.GetMinimumWaitTime();
        }

        /// <summary>
        /// Check to see that the minimum wait time is 0 for a defined process load.
        /// </summary>
        [Test]
        public void GetMinimumWhereZeroTest()
        {
            ProcessLoad processLoad = new ProcessLoad() 
            {
                new Process(1, 1, 0),
                new Process(2, 8, 3),
                new Process(3, 3, 2),
                new Process(4, 12, 3),
                new Process(5, 1, 2),
                new Process(6, 1, 4)
            };
            Runner actual = new Runner(processLoad, new ShortestJobFirst(1));

            actual.Run();

            Assert.AreEqual(0, actual.GetMinimumWaitTime());
        }

        /// <summary>
        /// Check to see that the correct value is returned.
        /// </summary>
        [Test]
        public void GetMaximumWaitTimeTest()
        {
            ProcessLoad processLoad = new ProcessLoad()
            {
                new Process(1, 3, 0),
                new Process(2, 6, 1),
                new Process(3, 1, 1),
                new Process(4, 8, 3)
            };
            Runner actual = new Runner(processLoad, new ShortestJobFirst(1));

            actual.Run();

            Assert.AreEqual(7, actual.GetMaximumWaitTime());
        }

        /// <summary>
        /// Check to see that the correct exception is thrown when calling the method before
        /// the simulation has been invoked.
        /// </summary>
        [Test]
        [ExpectedException(typeof(InvalidOperationException))]
        public void GetMaximumWaitTimeSimulationNotInvokedTest()
        {
            Runner actual = new Runner(10, 10, 10, new ShortestJobFirst(10));

            actual.GetMaximumWaitTime();
        }

        /// <summary>
        /// Check to see that the processes that should have been resumed have been.
        /// </summary>
        [Test]
        public void ProcessResumedTest()
        {
            ProcessLoad processLoad = new ProcessLoad()
            {
                new Process(1, 4, 0),
                new Process(2, 2, 1),
                new Process(3, 5, 2)
            };
            Runner runner = new Runner(processLoad, new RoundRobin(3));
            List<int> actual = new List<int>();
            List<int> expected = new List<int>()
            {
                1,
                3
            };

            runner.ProcessResumed += (o, e) => actual.Add(e.Id);
            runner.Run();

            for (int i = 0; i < expected.Count; i++)
            {
                Assert.AreEqual(expected[i], actual[i]);
            }
        }

        /// <summary>
        /// Check to see that the processes that should have been resumed have been.
        /// </summary>
        [Test]
        public void ProcessResumedSjfTest()
        {
            ProcessLoad processLoad = new ProcessLoad()
            {
                new Process(1, 1, 0),
                new Process(2, 8, 3),
                new Process(3, 3, 2),
                new Process(4, 12, 3),
                new Process(5, 1, 2),
                new Process(6, 1, 4)
            };
            Runner runner = new Runner(processLoad, new ShortestJobFirst(1));
            List<int> expected = new List<int>() { 3 };
            List<int> actual = new List<int>();

            runner.ProcessResumed += (o, e) => actual.Add(e.Id);
            runner.Run();

            for (int i = 0; i < expected.Count; i++)
            {
                Assert.AreEqual(expected[i], actual[i]);
            }
        }

        /// <summary>
        /// Check to see that the idle cpu time is correct.
        /// </summary>
        [Test]
        public void IdleCpuTimeFirstComeFirstServedTest()
        {
            ProcessLoad processLoad = new ProcessLoad()
            {
                new Process(1, 2, 2),
                new Process(2, 5, 8),
                new Process(3, 3, 3)
            };
            Runner actual = new Runner(processLoad, new FirstComeFirstServed());

            actual.Run();

            Assert.AreEqual(3, actual.IdleCpuTime);
        }

        /// <summary>
        /// Check to see that the correct time is returned.
        /// </summary>
        [Test]
        public void IdleCpuTimeRoundRobinTest()
        {
            ProcessLoad processLoad = new ProcessLoad()
            {
                new Process(1, 2, 1),
                new Process(2, 6, 13)
            };
            Runner actual = new Runner(processLoad, new RoundRobin(1));

            actual.Run();

            Assert.AreEqual(11, actual.IdleCpuTime);
        }

        /// <summary>
        /// Check to see that the correct time is returned.
        /// </summary>
        [Test]
        public void BusyCpuTimeTest()
        {
            ProcessLoad processLoad = new ProcessLoad()
            {
                new Process(1, 2, 1),
                new Process(2, 6, 13)
            };
            Runner actual = new Runner(processLoad, new RoundRobin(1));

            actual.Run();

            Assert.AreEqual(8, actual.BusyCpuTime);
        }

        /// <summary>
        /// Check to see that the predicate overload for avg wait time returns the correct
        /// value.
        /// </summary>
        [Test]
        public void AverageWaitTimePredicateTest()
        {
            ProcessLoad processLoad = new ProcessLoad()
            {
                new Process(1, 7, 0),
                new Process(2, 2, 1),
                new Process(3, 1, 2),
                new Process(4, 8, 3),
                new Process(5, 9, 4)
            };
            Runner actual = new Runner(processLoad, new FirstComeFirstServed());

            actual.Run();

            Assert.AreEqual((double)21 / 2, actual.GetWaitTimeMean(x => x.BurstTime > 7));
        }

        /// <summary>
        /// Check to see that the correct exception is raised when trying to call the get avg method before
        /// the simulation has been started.
        /// </summary>
        [Test]
        [ExpectedException(typeof(InvalidOperationException))]
        public void AverageWaitTimePredicateCalledBeforeSimulationStartedTest()
        {
            Runner actual = new Runner(2, 2, 2, new FirstComeFirstServed());

            actual.GetWaitTimeMean(x => x.Priority == Priority.High);
        }

        /// <summary>
        /// Check to see that the correct exception is thrown when the predicate function is null.
        /// </summary>
        [Test]
        [ExpectedException(typeof(ArgumentNullException))]
        public void AverageWaitTimePredicateNullTest()
        {
            Runner actual = new Runner(2, 2, 2, new FirstComeFirstServed());

            actual.Run();

            actual.GetWaitTimeMean(null);
        }

        /// <summary>
        /// Check to see that 0 is returned when no matches are found. 
        /// Note: this is to avoid a divison by 0.
        /// </summary>
        [Test]
        public void AverageWaitTimePredicateNoMatchesTest()
        {
            ProcessLoad processLoad = new ProcessLoad()
            {
                new Process(1, 7, 0),
                new Process(2, 2, 1),
                new Process(3, 1, 2),
                new Process(4, 8, 3),
                new Process(5, 9, 4)
            };
            Runner actual = new Runner(processLoad, new FirstComeFirstServed());

            actual.Run();

            Assert.AreEqual(0, actual.GetWaitTimeMean(x => x.Priority == Priority.High));
        }

        /// <summary>
        /// Check to see that the correct value for variance is returned.
        /// </summary>
        [Test]
        public void GetVarianceTest()
        {
            ProcessLoad processLoad = new ProcessLoad()
            {
                new Process(1, 6, 0),
                new Process(2, 1, 2),
                new Process(3, 8, 1),
                new Process(4, 9, 4)
            };
            Runner actual = new Runner(processLoad, new ShortestJobFirst(1));

            actual.Run();

            Assert.AreEqual((double)77 / 4, actual.GetWaitTimeVariance());
        }

        /// <summary>
        /// Check to see that the correct exception is raised when calling GetWaitTimeVariance before the simulation
        /// has started.
        /// </summary>
        [Test]
        [ExpectedException(typeof(InvalidOperationException))]
        public void GetVarianceSimulationNotStartedTest()
        {
            Runner actual = new Runner(10, 20, 10, new ShortestJobFirst(10));

            actual.GetWaitTimeVariance();
        }

        /// <summary>
        /// Check to see that the correct value is returned when calling GetWaitTimeStandardDeviation.
        /// </summary>
        [Test]
        public void GetStandardDeviationTest()
        {
            ProcessLoad processLoad = new ProcessLoad()
            {
                new Process(1, 6, 0),
                new Process(2, 1, 2),
                new Process(3, 8, 1),
                new Process(4, 9, 4)
            };
            Runner actual = new Runner(processLoad, new ShortestJobFirst(1));

            actual.Run();

            Assert.AreEqual(Math.Sqrt(actual.GetWaitTimeVariance()), actual.GetWaitTimeStandardDeviation());
        }

        /// <summary>
        /// Check to see that the correct exception is thrown when calling GetWaitTimeStandardDeviation before the simulation
        /// has started.
        /// </summary>
        [Test]
        [ExpectedException(typeof(InvalidOperationException))]
        public void GetStandardDeviationSimulationNotStartedTest()
        {
            Runner actual = new Runner(10, 10, 10, new FirstComeFirstServed());

            actual.GetWaitTimeStandardDeviation();
        }

        /// <summary>
        /// Check to see that the correct value is returned.
        /// </summary>
        [Test]
        public void GetVariancePredicateTest()
        {
            ProcessLoad processLoad = new ProcessLoad()
            {
                new Process(1, 8, 0),
                new Process(2, 7, 3),
                new Process(3, 5, 2)
            };
            Runner actual = new Runner(processLoad, new ShortestJobFirst(1));

            actual.Run();

            Assert.AreEqual((double)12.5 / 2, actual.GetWaitTimeVariance(x => x.BurstTime > 5));
        }

        /// <summary>
        /// Check to see that the correct value is returned when the predicate function has no matches.
        /// </summary>
        [Test]
        public void GetVarianceNoPredicateMatchesTest()
        {
            ProcessLoad processLoad = new ProcessLoad()
            {
                new Process(1, 8, 0),
                new Process(2, 7, 3),
                new Process(3, 5, 2)
            };
            Runner actual = new Runner(processLoad, new ShortestJobFirst(1));

            actual.Run();

            Assert.AreEqual(0, actual.GetWaitTimeVariance(x => x.Priority == Priority.High));
        }

        /// <summary>
        /// Check to see that the correct exception is thrown when you pass in a null predicate function.
        /// </summary>
        [Test]
        [ExpectedException(typeof(ArgumentNullException))]
        public void GetVariancePredicateNullTest()
        {
            ProcessLoad processLoad = new ProcessLoad()
            {
                new Process(1, 8, 0),
                new Process(2, 7, 3),
                new Process(3, 5, 2)
            };
            Runner actual = new Runner(processLoad, new ShortestJobFirst(1));
            Func<Process, bool> predicate = null;

            actual.Run();

            Assert.AreEqual(0, actual.GetWaitTimeVariance(predicate));
        }

        /// <summary>
        /// Check to make sure the correct exception is thrown when calling the variance method
        /// before the simulation has completed.
        /// </summary>
        [Test]
        [ExpectedException(typeof(InvalidOperationException))]
        public void GetVariancePredicateBeforeSimulationCompletedTest()
        {
            ProcessLoad processLoad = new ProcessLoad()
            {
                new Process(1, 8, 0),
                new Process(2, 7, 3),
                new Process(3, 5, 2)
            };
            Runner actual = new Runner(processLoad, new ShortestJobFirst(1));

            actual.GetWaitTimeVariance(x => x.WaitTime > 3);
        }

        /// <summary>
        /// Check to see that the correct value is returned.
        /// </summary>
        [Test]
        public void StandardDeviationPredicateTest()
        {
            ProcessLoad processLoad = new ProcessLoad()
            {
                new Process(1, 8, 0),
                new Process(2, 7, 3),
                new Process(3, 5, 2)
            };
            Runner actual = new Runner(processLoad, new ShortestJobFirst(1));

            actual.Run();

            Assert.AreEqual(2.5, actual.GetWaitTimeStandardDeviation(x => x.BurstTime > 5));
        }

        /// <summary>
        /// Check to see that the correct exception is thrown when std dev is called before
        /// the simulation has completed.
        /// </summary>
        [Test]
        [ExpectedException(typeof(InvalidOperationException))]
        public void StandardDeviationBeforeSimulationCompletedTest()
        {
            ProcessLoad processLoad = new ProcessLoad()
            {
                new Process(1, 8, 0),
                new Process(2, 7, 3),
                new Process(3, 5, 2)
            };
            Runner actual = new Runner(processLoad, new ShortestJobFirst(1));

            actual.GetWaitTimeStandardDeviation(x => x.WaitTime > 3);
        }

        /// <summary>
        /// Check to see that the correct exception is thrown when the predicate function is null.
        /// </summary>
        [Test]
        [ExpectedException(typeof(ArgumentNullException))]
        public void StandardDeviationPredicateNullTest()
        {
            Func<Process, bool> predicate = null;
            ProcessLoad processLoad = new ProcessLoad()
            {
                new Process(1, 8, 0),
                new Process(2, 7, 3),
                new Process(3, 5, 2)
            };
            Runner actual = new Runner(processLoad, new ShortestJobFirst(1));
            
            actual.Run();
            actual.GetWaitTimeStandardDeviation(predicate);
        }

        /// <summary>
        /// Check to see that the correct value is returned when calculating the % cpu utilization.
        /// </summary>
        [Test]
        public void GetCpuUtilizationTest()
        {
            ProcessLoad processLoad = new ProcessLoad()
                                          {
                                              new Process(1, 10, 12),
                                              new Process(2, 2, 10),
                                              new Process(3, 2, 3)
                                          };
            Runner actual = new Runner(processLoad, new ShortestJobFirst(1));

            actual.Run();

            Assert.AreEqual(63.64, actual.CpuUtilization);
        }

        /// <summary>
        /// Check to see that the correct exception is thrown when trying to get teh % cpu utilization time
        /// before the simulation has completed.
        /// </summary>
        [Test]
        [ExpectedException(typeof(InvalidOperationException))]
        public void GetCpuUtilizationInvokedBeforeSimulationCompletedTest()
        {
            Runner runner = new Runner(1, 2, 3, new FirstComeFirstServed());

            double actual = runner.CpuUtilization;
        }


        /// <summary>
        /// Check to see that the correct exception is thrown when no processes are specified in the simulation.
        /// </summary>
        [Test]
        [ExpectedException(typeof(ArgumentException))]
        public void NoProcessesSpecifiedTest()
        {
            Runner actual = new Runner(0, 0, 0, new FirstComeFirstServed());
        }

        /// <summary>
        /// Check to see that the mean turn around time is correct.
        /// </summary>
        [Test]
        public void TurnaroundTimeMeanTest()
        {
            ProcessLoad processLoad = new ProcessLoad
                                          {
                                              new Process(1, 8, 0),
                                              new Process(2, 3, 4),
                                              new Process(3, 13, 3)
                                          };
            Runner actual = new Runner(processLoad, new ShortestJobFirst(1));

            actual.Run();

            Assert.AreEqual((double)35/3, actual.GetTurnaroundTimeMean());
        }

        /// <summary>
        /// Check that the correct exception is thrown if trying to calculate mean turnaround time
        /// before simulation is complete.
        /// </summary>
        [Test]
        [ExpectedException(typeof(InvalidOperationException))]
        public void TurnaroundTimeMeanCalculatedBeforeSimulationCompleteTest()
        {
            ProcessLoad processLoad = new ProcessLoad
                                          {
                                              new Process(1, 8, 0),
                                              new Process(2, 3, 4),
                                              new Process(3, 13, 3)
                                          };
            Runner actual = new Runner(processLoad, new ShortestJobFirst(1));

            actual.GetTurnaroundTimeMean();
        }

        /// <summary>
        /// Check to see that hte correct value is returned when using a predicate function for turnaround time mean.
        /// </summary>
        [Test]
        public void TurnaroundTimePredicateMeanTest()
        {
            ProcessLoad processLoad = new ProcessLoad
                                          {
                                              new Process(1, 8, 0),
                                              new Process(2, 3, 4),
                                              new Process(3, 13, 3)
                                          };
            Runner actual = new Runner(processLoad, new ShortestJobFirst(1));

            actual.Run();

            Assert.AreEqual((double)32/2, actual.GetTurnaroundTimeMean(x => x.BurstTime > 3));
        }

        /// <summary>
        /// Check to see that the correct exception is thrown when trying to calculate the mean turnaround time before
        /// the simulation has completed.
        /// </summary>
        [Test]
        [ExpectedException(typeof(InvalidOperationException))]
        public void TurnaroundTimePredicateSimulationNotCompleteTest()
        {
            ProcessLoad processLoad = new ProcessLoad
                                          {
                                              new Process(1, 8, 0),
                                              new Process(2, 3, 4),
                                              new Process(3, 13, 3)
                                          };
            Runner actual = new Runner(processLoad, new ShortestJobFirst(1));

            actual.GetTurnaroundTimeMean(x => x.Priority == Priority.High);
        }

        /// <summary>
        /// Check to see that the correct exception is raised when the predicate function is null.
        /// </summary>
        [Test]
        [ExpectedException(typeof(ArgumentNullException))]
        public void TurnaroundTimePredicateNullTest()
        {
            ProcessLoad processLoad = new ProcessLoad
                                          {
                                              new Process(1, 8, 0),
                                              new Process(2, 3, 4),
                                              new Process(3, 13, 3)
                                          };
            Runner actual = new Runner(processLoad, new ShortestJobFirst(1));

            actual.Run();

            actual.GetTurnaroundTimeMean(null);
        }

        /// <summary>
        /// Check to see that the correct value is returned for the response time mean.
        /// </summary>
        [Test]
        public void ResponseTimeMeanTest()
        {
            ProcessLoad processLoad = new ProcessLoad
                                          {
                                              new Process(1, 8, 0),
                                              new Process(2, 3, 4),
                                              new Process(3, 13, 3)
                                          };
            Runner actual = new Runner(processLoad, new ShortestJobFirst(1));

            actual.Run();

            Assert.AreEqual((double) 8/3, actual.GetResponseTimeMean());
        }

        /// <summary>
        /// Check to see that the correct exception is thrown if calculated before the simulation has completed.
        /// </summary>
        [Test]
        [ExpectedException(typeof(InvalidOperationException))]
        public void ResponseTimeMeanBeforeSimulationCompleteTesT()
        {
            ProcessLoad processLoad = new ProcessLoad
                                          {
                                              new Process(1, 8, 0),
                                              new Process(2, 3, 4),
                                              new Process(3, 13, 3)
                                          };
            Runner actual = new Runner(processLoad, new ShortestJobFirst(1));

            actual.GetResponseTimeMean();
        }

        /// <summary>
        /// Check to see that the correct value is returned when calculating response time mean with a predicate function.
        /// </summary>
        [Test]
        public void ResponseTimeMeanPredicateTest()
        {
            ProcessLoad processLoad = new ProcessLoad
                                          {
                                              new Process(1, 8, 0),
                                              new Process(2, 3, 4),
                                              new Process(3, 13, 3)
                                          };
            Runner actual = new Runner(processLoad, new ShortestJobFirst(1));

            actual.Run();

            Assert.AreEqual((double) 8/2, actual.GetResponseTimeMean(x => x.BurstTime > 3));
        }

        /// <summary>
        /// Check to see that the correct exception is thrown when calculating the mean response time
        /// before the simulation has completed.
        /// </summary>
        [Test]
        [ExpectedException(typeof(InvalidOperationException))]
        public void ResponseTimePredicateMeanBeforeSimulationCompleteTest()
        {
            ProcessLoad processLoad = new ProcessLoad
                                          {
                                              new Process(1, 8, 0),
                                              new Process(2, 3, 4),
                                              new Process(3, 13, 3)
                                          };
            Runner actual = new Runner(processLoad, new ShortestJobFirst(1));

            actual.GetResponseTimeMean(x => x.Priority == Priority.High);
        }

        /// <summary>
        /// Check to see that the correct exception is thrown when the predicate function is null.
        /// </summary>
        [Test]
        [ExpectedException(typeof(ArgumentNullException))]
        public void ResponseTimePredicateNullTest()
        {
            ProcessLoad processLoad = new ProcessLoad
                                          {
                                              new Process(1, 8, 0),
                                              new Process(2, 3, 4),
                                              new Process(3, 13, 3)
                                          };
            Runner actual = new Runner(processLoad, new ShortestJobFirst(1));

            actual.Run();

            actual.GetResponseTimeMean(null);
        }

        /// <summary>
        /// Check to see that the correct value is returned.
        /// </summary>
        [Test]
        public void GetBurstTimeMeanTest()
        {
            ProcessLoad processLoad = new ProcessLoad()
                                          {
                                              new Process(1, 12, 0),
                                              new Process(2, 13, 0),
                                              new Process(3, 3, 0),
                                              new Process(4, 9, 0)
            };
            Runner actual = new Runner(processLoad, new FirstComeFirstServed());

            actual.Run();

            Assert.AreEqual((double)37/4, actual.GetBurstTimeMean());
        }

        /// <summary>
        /// Check to see that the correct value is returned.
        /// </summary>
        [Test]
        public void GetThroughputMeanTest()
        {
            ProcessLoad processLoad = new ProcessLoad()
                                          {
                                              new Process(1, 12, 0),
                                              new Process(2, 13, 0),
                                              new Process(3, 3, 0),
                                              new Process(4, 9, 0)
            };
            Runner actual = new Runner(processLoad, new FirstComeFirstServed());

            actual.Run();

            Assert.AreEqual(2, actual.GetThroughputMean());
        }

        /// <summary>
        /// Check to see that the correct value is returned.
        /// </summary>
        [Test]
        public void GetThroughputMeanSjfTest()
        {
            ProcessLoad processLoad = new ProcessLoad()
            {
                new Process(1, 12, 0),
                new Process(2, 17, 1),
                new Process(3, 8, 8),
                new Process(4, 1, 3),
                new Process(5, 3, 7)
            };
            Runner actual = new Runner(processLoad, new ShortestJobFirst(1));

            actual.Run();

            Assert.AreEqual(2.5, actual.GetThroughputMean());
        }

        /// <summary>
        /// Check to see the correct value is returned.
        /// </summary>
        [Test]
        public void WindowTimeframeTest()
        {
            ProcessLoad processLoad = new ProcessLoad()
            {
                new Process(1, 12, 0),
                new Process(2, 17, 1),
                new Process(3, 8, 8),
                new Process(4, 1, 3),
                new Process(5, 3, 7)
            };
            Runner actual = new Runner(processLoad, new ShortestJobFirst(1));

            Assert.AreEqual(32, actual.WindowTimeframe);
        }

        /// <summary>
        /// Check to see that the correct exception is raised if trying to calculate the mean
        /// throughput before the simulation has completed.
        /// </summary>
        [Test]
        [ExpectedException(typeof(InvalidOperationException))]
        public void GetThroughputBeforeSimulationCompletedTest()
        {
            ProcessLoad processLoad = new ProcessLoad()
            {
                new Process(1, 12, 0),
                new Process(2, 17, 1),
                new Process(3, 8, 8),
                new Process(4, 1, 3),
                new Process(5, 3, 7)
            };
            Runner actual = new Runner(processLoad, new ShortestJobFirst(1));

            actual.GetThroughputMean();
        }

        /// <summary>
        /// Check to see that throughput supports auto gen loads as well.
        /// </summary>
        [Test]
        public void GetThroughputAutoGenLoadTest()
        {
            Runner actual = new Runner(10, 10, 10, new ShortestJobFirstExpert(2, 1));

            actual.Run();

            Assert.AreNotEqual(0, actual.GetThroughputMean());
        }

        /// <summary>
        /// Check to see that the correct number of windows is returned.
        /// </summary>
        [Test]
        public void ThroughputWindowsTest()
        {
            ProcessLoad processLoad = new ProcessLoad()
            {
                new Process(1, 12, 0),
                new Process(2, 17, 1),
                new Process(3, 8, 8),
                new Process(4, 1, 3),
                new Process(5, 3, 7)
            };
            Runner actual = new Runner(processLoad, new ShortestJobFirst(1));
 
            actual.Run();

            Assert.AreEqual(2, actual.ThroughputWindows.Count);
        }

        /// <summary>
        /// The values of the throughput windows are only vlaid upon simulation completion.
        /// </summary>
        [Test]
        [ExpectedException(typeof(InvalidOperationException))]
        public void ThroughputWindowsBeforeSimulationCompletedTest()
        {
            Runner runner = new Runner(1, 2, 3, new FirstComeFirstServed());

            var actual = runner.ThroughputWindows;
        }

        /// <summary>
        /// Check to see that the correct value is returned.
        /// </summary>
        [Test]
        public void GetResponseTimeVarianceTest()
        {
            ProcessLoad processLoad = new ProcessLoad()
            {
                new Process(1, 12, 3),
                new Process(2, 8, 8),
                new Process(3, 9, 2),
                new Process(4, 6, 23)
            };
            Runner actual = new Runner(processLoad, new ShortestJobFirst(1));

            actual.Run();

            Assert.AreEqual(43.6875, actual.GetResponseTimeVariance());
        }

        /// <summary>
        /// Check to make sure that the correct exception is thrown when trying to calculate the
        /// response time variance before the simulation has completed.
        /// </summary>
        [Test]
        [ExpectedException(typeof(InvalidOperationException))]
        public void GetResponseTimeVarianceBeforeSimulationCompletedTest()
        {
            ProcessLoad processLoad = new ProcessLoad()
            {
                new Process(1, 12, 3),
                new Process(2, 8, 8),
                new Process(3, 9, 2),
                new Process(4, 6, 23)
            };
            Runner actual = new Runner(processLoad, new ShortestJobFirst(1));

            actual.GetResponseTimeVariance();
        }

        /// <summary>
        /// Check to see that the correct value is returned.
        /// </summary>
        [Test]
        public void GetResponseTimeStandardDeviationTest()
        {
            ProcessLoad processLoad = new ProcessLoad()
            {
                new Process(1, 12, 3),
                new Process(2, 8, 8),
                new Process(3, 9, 2),
                new Process(4, 6, 23)
            };
            Runner actual = new Runner(processLoad, new ShortestJobFirst(1));

            actual.Run();

            Assert.AreEqual(6.61, Math.Round(actual.GetResponseTimeStandardDeviation(), 2));
        }

        /// <summary>
        /// Check to see that the correct exception is thrown.
        /// </summary>
        [Test]
        [ExpectedException(typeof(InvalidOperationException))]
        public void GetResponseTimeStandardDeviationBeforeSimulationCompletionTest()
        {
            ProcessLoad processLoad = new ProcessLoad()
            {
                new Process(1, 12, 3),
                new Process(2, 8, 8),
                new Process(3, 9, 2),
                new Process(4, 6, 23)
            };
            Runner actual = new Runner(processLoad, new ShortestJobFirst(1));

            Assert.AreEqual(6.61, Math.Round(actual.GetResponseTimeStandardDeviation(), 2));
        }

        /// <summary>
        /// Check to see that the correct value is returned.
        /// </summary>
        [Test]
        public void GetResponseTimeVariancePredicateTest()
        {
            ProcessLoad processLoad = new ProcessLoad()
            {
                new Process(1, 12, 0),
                new Process(2, 32, 8),
                new Process(3, 8, 2),
                new Process(4, 2, 14),
                new Process(5, 7, 17)
            };
            Runner actual = new Runner(processLoad, new ShortestJobFirst(1));

            actual.Run();

            Assert.AreEqual(110.25, actual.GetResponseTimeVariance(x => x.BurstTime > 8));
        }

        /// <summary>
        /// Check to make sure that the correct exception is raised.
        /// </summary>
        [Test]
        [ExpectedException(typeof(InvalidOperationException), ExpectedMessage = "The response time variance can only be calculated upon simulation completion.")]
        public void GetResponseTimeVariancePredicateBeforeSimulationCompletedTest()
        {
            ProcessLoad processLoad = new ProcessLoad()
            {
                new Process(1, 12, 0),
                new Process(2, 32, 8),
                new Process(3, 8, 2),
                new Process(4, 2, 14),
                new Process(5, 7, 17)
            };
            Runner actual = new Runner(processLoad, new ShortestJobFirst(1));

            actual.GetResponseTimeVariance(x => x.Priority == Priority.High);
        }

        /// <summary>
        /// Check to see that the correct value is returned.
        /// </summary>
        [Test]
        public void GetResponseTimeStandardDeviationPredicateTest()
        {
            ProcessLoad processLoad = new ProcessLoad()
            {
                new Process(1, 12, 0),
                new Process(2, 32, 8),
                new Process(3, 8, 2),
                new Process(4, 2, 14),
                new Process(5, 7, 17)
            };
            Runner actual = new Runner(processLoad, new ShortestJobFirst(1));

            actual.Run();

            Assert.AreEqual(10.5, actual.GetResponseTimeStandardDeviation(x => x.BurstTime > 8));
        }

        /// <summary>
        /// Check to see that the correct value is returned.
        /// </summary>
        [Test]
        public void GetTurnaroundTimeVarianceTest()
        {
            ProcessLoad processLoad = new ProcessLoad()
            {
                new Process(1, 12, 0),
                new Process(2, 32, 8),
                new Process(3, 8, 2),
                new Process(4, 2, 14),
                new Process(5, 7, 17)
            };
            Runner actual = new Runner(processLoad, new ShortestJobFirst(1));

            actual.Run();

            Assert.AreEqual(324.64, actual.GetTurnaroundTimeVariance());
        }

        /// <summary>
        /// Check to make sure the correct exception is thrown.
        /// </summary>
        [Test]
        [ExpectedException(typeof(InvalidOperationException), ExpectedMessage = "The response time standard deviation can " + 
            "only be calculated upon simulation completion.")]
        public void GetResponseTimeStandardDeviationPedicateSimulationNotCompleteTest()
        {
            ProcessLoad processLoad = new ProcessLoad()
            {
                new Process(1, 12, 0),
                new Process(2, 32, 8),
                new Process(3, 8, 2),
                new Process(4, 2, 14),
                new Process(5, 7, 17)
            };
            Runner actual = new Runner(processLoad, new ShortestJobFirst(1));

            actual.GetResponseTimeStandardDeviation(x => x.BurstTime >= 23);
        }

        /// <summary>
        /// Check to see that the correct exception is thrown.
        /// </summary>
        [Test]
        [ExpectedException(typeof(InvalidOperationException))]
        public void GetTurnaroundTimeVarianceSimulationNotCompletedTest()
        {
            ProcessLoad processLoad = new ProcessLoad()
            {
                new Process(1, 12, 0),
                new Process(2, 32, 8),
                new Process(3, 8, 2),
                new Process(4, 2, 14),
                new Process(5, 7, 17)
            };
            Runner actual = new Runner(processLoad, new ShortestJobFirst(1));

            actual.GetTurnaroundTimeVariance();
        }

        /// <summary>
        /// Check to see that the correct value is returned.
        /// </summary>
        [Test]
        public void GetTurnaroundTimeStandardDeviationTest()
        {
            ProcessLoad processLoad = new ProcessLoad()
            {
                new Process(1, 12, 0),
                new Process(2, 32, 8),
                new Process(3, 8, 2),
                new Process(4, 2, 14),
                new Process(5, 7, 17)
            };
            Runner actual = new Runner(processLoad, new ShortestJobFirst(1));

            actual.Run();

            Assert.AreEqual(18.02, Math.Round(actual.GetTurnaroundTimeStandardDeviation(), 2));
        }

        /// <summary>
        /// Check to see that the correct exception is raised.
        /// </summary>
        [Test]
        [ExpectedException(typeof(InvalidOperationException), ExpectedMessage = "The turnaround time standard deviation can" + 
            " only be calculated upon simulation completion.")]
        public void GetTurnaroundTimeStandardDeviationSimulationNotCompletedTest()
        {
            ProcessLoad processLoad = new ProcessLoad()
            {
                new Process(1, 12, 0),
                new Process(2, 32, 8),
                new Process(3, 8, 2),
                new Process(4, 2, 14),
                new Process(5, 7, 17)
            };
            Runner actual = new Runner(processLoad, new ShortestJobFirst(1));

            actual.GetTurnaroundTimeStandardDeviation();
        }

        /// <summary>
        /// Check to see that the correct exception is thrown.
        /// </summary>
        [Test]
        [ExpectedException(typeof(ArgumentNullException))]
        public void GetResponseTimeVariancePredicateNullTest()
        {
            Runner actual = new Runner(1, 2, 3, new FirstComeFirstServed());

            actual.Run();

            actual.GetResponseTimeVariance(null);
        }

        /// <summary>
        /// Check to see that the correct exception is thrown.
        /// </summary>
        [Test]
        [ExpectedException(typeof(ArgumentNullException))]
        public void GetResponseTimePredicateNullTest()
        {
            Runner actual = new Runner(1, 2, 3, new FirstComeFirstServed());

            actual.Run();

            actual.GetResponseTimeStandardDeviation(null);
        }

        /// <summary>
        /// Check to see that the correct value is returned.
        /// </summary>
        [Test]
        public void GetTurnarounTimeVariancePredicateTest()
        {
            ProcessLoad processLoad = new ProcessLoad()
                {
                    new Process(1, 12, 0),
                    new Process(2, 6, 3),
                    new Process(3, 1, 8),
                    new Process(4, 3, 2)
                };
            Runner actual = new Runner(processLoad, new ShortestJobFirst(1));

            actual.Run();

            Assert.AreEqual(42.25, actual.GetTurnaroundTimeVariance(x => x.BurstTime > 3));
        }

        /// <summary>
        /// Check to see that the correct exception is thrown.
        /// </summary>
        [Test]
        [ExpectedException(typeof(InvalidOperationException))]
        public void GetTurnaroundTimeVariancePredicateBeforeSimulationCompletedTest()
        {
            Runner actual = new Runner(1, 2, 3, new FirstComeFirstServed());

            actual.GetTurnaroundTimeVariance(x => x.BurstTime > 9);
        }

        /// <summary>
        /// Check to see that the correct exception is thrown.
        /// </summary>
        [Test]
        [ExpectedException(typeof(ArgumentNullException))]
        public void GetTurnaroundTimeVariancePredicateNullTest()
        {
            Runner actual = new Runner(1, 2, 3, new FirstComeFirstServed());

            actual.Run();

            actual.GetTurnaroundTimeVariance(null);
        }

        /// <summary>
        /// Check to see that the correct value is returned.
        /// </summary>
        [Test]
        public void GetTurnaroundTimeStandardDeviationPredicateTest()
        {
            ProcessLoad processLoad = new ProcessLoad()
                {
                    new Process(1, 12, 0),
                    new Process(2, 6, 3),
                    new Process(3, 1, 8),
                    new Process(4, 3, 2)
                };
            Runner actual = new Runner(processLoad, new ShortestJobFirst(1));

            actual.Run();

            Assert.AreEqual(6.5, actual.GetTurnaroundTimeStandardDeviation(x => x.BurstTime > 3));
        }

        /// <summary>
        /// Check to see that the correct exception is thrown.
        /// </summary>
        [Test]
        [ExpectedException(typeof(InvalidOperationException), ExpectedMessage = "The turnaround time standard deviation " + 
            "can only be calculated upon simulation completion.")]
        public void GetTurnaroundTimeStandardDeviationPredicateSimulationNotCompleteTest()
        {
            Runner actual = new Runner(1, 2, 3, new FirstComeFirstServed());

            actual.GetTurnaroundTimeStandardDeviation(x => x.Priority == Priority.High);
        }

        /// <summary>
        /// Check to see that the correct value is returned.
        /// </summary>
        [Test]
        public void CustomThroughputWindowManualMeanTest()
        {
            ProcessLoad processLoad = new ProcessLoad()
            {
                new Process(1, 12, 0),
                new Process(2, 6, 4),
                new Process(3, 2, 2),
                new Process(4, 1, 1)
            };
            Runner actual = new Runner(processLoad, new ShortestJobFirst(1), 10);

            actual.Run();

            Assert.AreEqual(1.33, Math.Round(actual.GetThroughputMean(), 2));
        }

        /// <summary>
        /// Check to see that the correct exception is thrown.
        /// </summary>
        [Test]
        [ExpectedException(typeof(ArgumentOutOfRangeException))]
        public void CustomThroughputWindowManualLessThanOneTest()
        {
            ProcessLoad processLoad = new ProcessLoad()
            {
                new Process(1, 12, 0),
                new Process(2, 6, 4),
                new Process(3, 2, 2),
                new Process(4, 1, 1)
            };
            Runner actual = new Runner(processLoad, new ShortestJobFirst(1), -1);
        }

        /// <summary>
        /// Check to make sure that the correct value is returned.
        /// </summary>
        [Test]
        public void CustomThroughputWindowAutomatedTest()
        {
            Runner actual = new Runner(1, 2, 3, new FirstComeFirstServed(), 12);

            Assert.AreEqual(12, actual.WindowTimeframe);
        }

        /// <summary>
        /// Check to see that the correct exception is thrown.
        /// </summary>
        [Test]
        [ExpectedException(typeof(ArgumentOutOfRangeException))]
        public void CustomThroughputWindowAutomatedLessThanOneTest()
        {
            Runner actual = new Runner(1, 2, 3, new FirstComeFirstServed(), -1);

            actual.Run();
        }

        /// <summary>
        /// Check to see that the correct value is returned.
        /// </summary>
        [Test]
        public void GetThroughputVarianceTest()
        {
            ProcessLoad processLoad = new ProcessLoad()
            {
                new Process(1, 8, 0),
                new Process(2, 12, 8),
                new Process(3, 3, 12),
                new Process(4, 3, 1),
                new Process(5, 6, 7)
            };
            Runner actual = new Runner(processLoad, new ShortestJobFirst(1), 10);

            actual.Run();

            Assert.AreEqual(1.1875, actual.GetThroughputVariance());
        }

        /// <summary>
        /// Check to see that the correct exception is thrown.
        /// </summary>
        [Test]
        [ExpectedException(typeof(InvalidOperationException), ExpectedMessage = "The throughput variance can only be calculated upon simulation completion.")]
        public void GetThroughputVarianceBeforeSimulationCompleteTest()
        {
            ProcessLoad processLoad = new ProcessLoad()
            {
                new Process(1, 8, 0),
                new Process(2, 12, 8),
                new Process(3, 3, 12),
                new Process(4, 3, 1),
                new Process(5, 6, 7)
            };
            Runner actual = new Runner(processLoad, new ShortestJobFirst(1), 10);

            Assert.AreEqual(1.1875, actual.GetThroughputVariance());
        }

        /// <summary>
        /// Check to see that the correct value is returned.
        /// </summary>
        [Test]
        public void GetThroughputStandardDeviationTest()
        {
            ProcessLoad processLoad = new ProcessLoad()
            {
                new Process(1, 8, 0),
                new Process(2, 12, 8),
                new Process(3, 3, 12),
                new Process(4, 3, 1),
                new Process(5, 6, 7)
            };
            Runner actual = new Runner(processLoad, new ShortestJobFirst(1), 10);

            actual.Run();

            Assert.AreEqual(1.09, Math.Round(actual.GetThroughputStandardDeviation(), 2));
        }

        /// <summary>
        /// Check to see that the correct exception is thrown.
        /// </summary>
        [Test]
        [ExpectedException(typeof(InvalidOperationException), ExpectedMessage = "The throughput standard deviation can only be calculated " + 
            "upon simulation completion.")]
        public void GetThroughputStandardDeviationBeforeSimulationCompleteTest()
        {
            ProcessLoad processLoad = new ProcessLoad()
            {
                new Process(1, 8, 0),
                new Process(2, 12, 8),
                new Process(3, 3, 12),
                new Process(4, 3, 1),
                new Process(5, 6, 7)
            };
            Runner actual = new Runner(processLoad, new ShortestJobFirst(1), 10);

            Assert.AreEqual(1.09, Math.Round(actual.GetThroughputStandardDeviation(), 2));
        }
    }

}
